package com.lagou.edu.factory;

import com.lagou.edu.factory.anno.*;
import com.lagou.edu.utils.PackageUtil;
import com.lagou.edu.utils.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author schhx
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class AnnotationBeanFactory {

    private static Set<Class> ANNO = new HashSet<>();

    private static Map<String, Object> beanMap = new HashMap<>();


    static {
        Collections.addAll(ANNO, Component.class, Controller.class, Service.class, Repository.class);

        try {
            List<Class> classList = PackageUtil.getClass("com.lagou.edu");
            for (Class clazz : classList) {
                String beanName = getBeanName(clazz);
                if (StringUtils.isNotEmpty(beanName)) {
                    Object instance = clazz.newInstance();
                    beanMap.put(beanName, instance);
                }
            }

            for (Object o : beanMap.values()) {
                Field[] declaredFields = o.getClass().getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    if (declaredField.getAnnotation(Autowired.class) != null) {
                        declaredField.setAccessible(true);
                        declaredField.set(o, getBeanByType(declaredField.getType()));
                    }
                }
            }

            for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
                if (entry.getValue().getClass().getAnnotation(Transactional.class) != null) {
                    Class<?>[] interfaces = entry.getValue().getClass().getInterfaces();
                    if (interfaces != null && interfaces.length > 0) {
                        beanMap.put(entry.getKey(), getBeanByType(ProxyFactory.class).getJdkProxy(entry.getValue()));
                    } else {
                        beanMap.put(entry.getKey(), getBeanByType(ProxyFactory.class).getCglibProxy(entry.getValue()));
                    }
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

    private static String getBeanName(Class clazz) {
        if (ANNO.contains(clazz)) {
            return null;
        }
        Annotation annotation = clazz.getAnnotation(Component.class);
        boolean isBean = false;
        String value = null;
        if (annotation != null) {
            value = ((Component) annotation).value();
            isBean = true;
        }
        annotation = clazz.getAnnotation(Controller.class);
        if (annotation != null) {
            value = ((Controller) annotation).value();
            isBean = true;
        }
        annotation = clazz.getAnnotation(Service.class);
        if (annotation != null) {
            value = ((Service) annotation).value();
            isBean = true;
        }
        annotation = clazz.getAnnotation(Repository.class);
        if (annotation != null) {
            value = ((Repository) annotation).value();
            isBean = true;
        }

        return !isBean ? null : StringUtils.isNotEmpty(value) ? value : StringUtils.toLowerCaseFirstOne(clazz.getSimpleName());

    }


    public static Object getBean(String id) {
        return beanMap.get(id);
    }

    public static <T> T getBeanByType(Class<T> type) {
        for (Object o : beanMap.values()) {
            if (type.isAssignableFrom(o.getClass())) {
                return (T) o;
            }
        }
        return null;
    }

}
